package com.openlogic.common.plugins.db.api.datasource;

import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//import org.springframework.jdbc.core.JdbcTemplate;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Sets;
import com.openlogic.common.plugins.db.api.dto.BaseDataSourceParamDto;
import com.openlogic.common.plugins.db.api.utils.JSONUtils;
import com.openlogic.common.plugins.db.api.utils.PasswordUtils;
import com.openlogic.common.plugins.spi.enums.DbType;
import com.openlogic.datasource.api.param.BaseConnectionParam;
import com.openlogic.datasource.api.param.ConnectionParam;
import com.zaxxer.hikari.HikariDataSource;

import jodd.db.DbQuery;
import jodd.db.connection.DataSourceConnectionProvider;

/**
 * @ClassName: AbstractDataSourceProcessor
 * @Description: 抽象数据源处理器
 * @author weiyongpeng
 * @date 2024年11月16日 上午10:37:01
 */
public abstract class AbstractDataSourceProcessor implements DataSourceProcessor {
	
	private volatile DataSourceConnectionProvider connectionProvider;
	
	private Map<String, HikariDataSource> sourceMap = new ConcurrentHashMap<>();
	
	private HikariDataSource dataSource = new HikariDataSource();

	private static final Logger log = LoggerFactory.getLogger(AbstractDataSourceProcessor.class);

	private static final Pattern DATABASE_PATTER = Pattern.compile("^[a-zA-Z0-9\\_\\-\\.]+$");

	private static final Pattern PARAMS_PATTER = Pattern.compile("^[a-zA-Z0-9\\-\\_\\/\\@\\.\\:]+$");

	private static final Set<String> POSSIBLE_MALICIOUS_KEYS = Sets.newHashSet("allowLoadLocalInfile");

	@Override
	public void checkDatasourceParam(BaseDataSourceParamDto dataSourceParam) {
		log.info("start check jdbcParam >>>>");
		checkDatabasePatter(dataSourceParam.getDatabase());
		checkOther(dataSourceParam.getOther());
	}
	
	/**
	* 描述：检查数据库名称是否合规
	* @Title: checkDatabasePatter
	* @param database
	* @author weiyongpeng
	* @date  2024年11月16日 上午10:39:41
	 */
    protected void checkDatabasePatter(String database) {
        if (!DATABASE_PATTER.matcher(database).matches()) {
            throw new IllegalArgumentException("database name illegal");
        }
    }
    
    @Override
    public String getDatasourceUniqueId(ConnectionParam connectionParam, DbType dbType) {
        BaseConnectionParam baseConnectionParam = (BaseConnectionParam) connectionParam;
        return MessageFormat.format("{0}@{1}@{2}@{3}", dbType.getDesc(), baseConnectionParam.getUser(),
                PasswordUtils.encodePassword(baseConnectionParam.getPassword()), baseConnectionParam.getJdbcUrl());
    }
    
    @Override
	public DataSourceConnectionProvider createDataSource(ConnectionParam connectionParam) {
    	if (connectionProvider == null) {
			synchronized (DbQuery.class) {
				if (connectionProvider == null && sourceMap.get(connectionParam.getUser()+connectionParam.getPassword()+connectionParam.getJdbcUrl()) != null) {
					connectionProvider = new DataSourceConnectionProvider(sourceMap.get(connectionParam.getUser()+connectionParam.getPassword()+connectionParam.getJdbcUrl()));
				}else {
					dataSource.setJdbcUrl(getJdbcUrl(connectionParam));
			    	String user = connectionParam.getUser();
			    	String password = PasswordUtils.decodePassword(connectionParam.getPassword());
			    	dataSource.setUsername(user);
			    	dataSource.setPassword(password);
			    	sourceMap.put(user+password+connectionParam.getJdbcUrl(), dataSource);
			    	connectionProvider = new DataSourceConnectionProvider(dataSource);
				}
			}
		}
        return connectionProvider;
	}

	/**
    * 描述：检查其他参数是否合规 
    * @Title: checkOther
    * @param other
    * @author weiyongpeng
    * @date  2024年11月16日 上午10:40:37
     */
    protected void checkOther(Map<String, String> other) {
        if (MapUtils.isEmpty(other)) {
            return;
        }

        if (!Sets.intersection(other.keySet(), POSSIBLE_MALICIOUS_KEYS).isEmpty()) {
            throw new IllegalArgumentException("Other params include possible malicious keys.");
        }

        for (Map.Entry<String, String> entry : other.entrySet()) {
            if (!PARAMS_PATTER.matcher(entry.getKey()).matches()) {
                throw new IllegalArgumentException("datasource other params: " + entry.getKey() + " illegal");
            }
        }
    }
    
    protected Map<String, String> transformOtherParamToMap(String other) {
        if (StringUtils.isBlank(other)) {
            return Collections.emptyMap();
        }
        return JSONUtils.parseObject(other, new TypeReference<Map<String, String>>() {
        });
    }
}
